#pragma once
#include <string>
#include "Util.hpp"
#include "HttpRequest.hpp"
#include "../Buffer.hpp"

#define CONTEXT_MAX_LINE 8192
typedef enum
{
    RECV_HTTP_ERROR,
    RECV_HTTP_LINE,
    RECV_HTTP_HEAD,
    RECV_HTTP_BODY,
    RECV_HTTP_OVER
} HttpRecvStatu;
class HttpContext
{
private:
    int _resp_statu;           // 响应状态码
    HttpRecvStatu _recv_statu; // 当前接收及解析的阶段状态
    HttpRequest _request;      // 已经解析得到的请求信息

    bool RecvHttpLine(Buffer *buf)
    {
        if (_recv_statu != RECV_HTTP_LINE)
            return false;
        std::string line = buf->GetLine();
        // 数据不足一行
        if (line.size() == 0)
        {
            // 414 URI Too Long
            if (buf->ReadAbleSize() > CONTEXT_MAX_LINE)
            {
                _resp_statu = 414;
                _recv_statu = RECV_HTTP_ERROR;
                return false;
            }
            // 数据不足一行，再等等
            return true;
        }
        // 414 URI Too Long
        if (line.size() > CONTEXT_MAX_LINE)
        {
            _resp_statu = 414;
            _recv_statu = RECV_HTTP_ERROR;
            return false;
        }
        if (!ParseHttpLine(line))
            return false;
        buf->ConfirmReadSize(line.size());
        // 首行处理完毕，进入头部获取阶段
        _recv_statu = RECV_HTTP_HEAD;
        return true;
    }
    bool ParseHttpLine(const std::string &line)
    {
        std::smatch matches;
        std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
        bool ret = std::regex_match(line, matches, e);
        // 400 Bad Request
        if (ret == false)
        {
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 400;
            return false;
        }
        // matches
        // 0 : GET /bitejiuyeke/login?user=xiaoming&pass=123123 HTTP/1.1
        // 1 : GET
        // 2 : /bitejiuyeke/login
        // 3 : user=xiaoming&pass=123123
        // 4 : HTTP/1.1
        // 请求方法
        std::string method = matches[1];
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
        _request._method = method;
        // 资源路径
        _request._path = Util::URLDecode(matches[2], false);
        // 协议版本
        _request._version = matches[4];
        // 查询字符串
        // 格式：key1=val1&key2=val2 ...
        std::vector<std::string> query_string_arry;
        std::string query_string = matches[3];
        Util::Spliter(query_string, "&", &query_string_arry);
        for (auto &str : query_string_arry)
        {
            size_t pos = str.find('=');
            // 400 Bad Request
            if (pos == std::string::npos)
            {
                _recv_statu = RECV_HTTP_ERROR;
                _resp_statu = 400;
                return false;
            }
            std::string key = Util::URLDecode(str.substr(0, pos), true);
            std::string val = Util::URLDecode(str.substr(pos + 1), true);
            _request.SetParam(key, val);
        }
        return true;
    }
    bool RecvHttpHead(Buffer *buf)
    {
        if (_recv_statu != RECV_HTTP_HEAD)
            return false;
        // 一行一行取出数据，直到遇到空行为止
        // 格式 key: val\r\nkey: val\r\n ...
        while (true)
        {
            std::string line = buf->GetLine();
            // 数据不足一行
            if (line.size() == 0)
            {
                // 414 URI Too Long
                if (buf->ReadAbleSize() > CONTEXT_MAX_LINE)
                {
                    _resp_statu = 414;
                    _recv_statu = RECV_HTTP_ERROR;
                    return false;
                }
                // 数据不足一行，再等等
                return true;
            }
            // 414 URI Too Long
            if (line.size() > CONTEXT_MAX_LINE)
            {
                _resp_statu = 414;
                _recv_statu = RECV_HTTP_ERROR;
                return false;
            }
            if (line == "\r\n" || line == "\n")
            {
                buf->ConfirmReadSize(line.size());
                break;
            }
            buf->ConfirmReadSize(line.size());
            if (!ParseHttpHead(line))
                return false;
        }
        // 头部处理完毕，进入正文获取阶段
        _recv_statu = RECV_HTTP_BODY;
        return true;
    }
    bool ParseHttpHead(std::string &line)
    {
        // 格式 key: val\r\n
        if (line.back() == '\n')
            line.pop_back();
        if (line.back() == '\r')
            line.pop_back();
        size_t pos = line.find(": ");
        // 400 Bad Request
        if (pos == std::string::npos)
        {
            _recv_statu = RECV_HTTP_ERROR;
            _resp_statu = 400;
            return false;
        }
        std::string key = line.substr(0, pos);
        std::string val = line.substr(pos + 2);
        _request.SetHeader(key, val);
        return true;
    }
    bool RecvHttpBody(Buffer *buf)
    {
        if (_recv_statu != RECV_HTTP_BODY)
            return false;
        // 获取正文长度
        size_t contentLength = _request.ContentLength();
        // 没有正文，则请求接收解析完毕
        if (contentLength == 0)
        {
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }
        // 计算实际还需要接收的正文长度
        size_t realLen = contentLength - _request._body.size();
        // 缓冲区中数据，包含了当前请求的所有正文，取出所需的数据
        if (buf->ReadAbleSize() >= realLen)
        {
            _request._body.append(buf->ReadBegin(), realLen);
            buf->ConfirmReadSize(realLen);
            _recv_statu = RECV_HTTP_OVER;
            return true;
        }
        // 缓冲区中数据，无法满足当前正文的需要，数据不足，取出全部数据，然后等待新数据到来
        _request._body.append(buf->ReadBegin(), buf->ReadAbleSize());
        buf->ConfirmReadSize(buf->ReadAbleSize());
        return true;
    }

public:
    HttpContext() : _resp_statu(200), _recv_statu(RECV_HTTP_LINE) {}
    ~HttpContext() {}
    void ReSet()
    {
        _resp_statu = 200;
        _recv_statu = RECV_HTTP_LINE;
        _request.ReSet();
    }
    int RespStatu() { return _resp_statu; }
    HttpRecvStatu RecvStatu() { return _recv_statu; }
    HttpRequest &Request() { return _request; }
    // 接收并解析HTTP请求
    void RecvHttpRequest(Buffer *buf)
    {
        switch (_recv_statu)
        {
        case RECV_HTTP_LINE:
            RecvHttpLine(buf);
        case RECV_HTTP_HEAD:
            RecvHttpHead(buf);
        case RECV_HTTP_BODY:
            RecvHttpBody(buf);
        }
    }
};